;*********************************************************************
; contains entry points for temporary ISRs and CPU exception handlers
;
; Author: Aidan Goddard 10/5/13
;*********************************************************************

; external pointers
extern Printf
extern g_ticks

; cpu exceptions
global CPU_exception_DE
global CPU_exception_DB
global CPU_exception_NMI
global CPU_exception_BP
global CPU_exception_OF
global CPU_exception_BR
global CPU_exception_UD
global CPU_exception_NM
global CPU_exception_DF
global CPU_exception_TS
global CPU_exception_NP
global CPU_exception_SS
global CPU_exception_GP
global CPU_exception_PF
global CPU_exception_MF
global CPU_exception_AC
global CPU_exception_MC
global CPU_exception_XF
global CPU_exception_SX

; APIC timer calibration IRQs
global IRQ_spurious_APIC
global IRQ_dummy_APIC
global IRQ_APIC_PIT_calibrator

;*********************************************************************
section .text

;*********************************************************************
; CPU exceptions

; divide by zero
CPU_exception_DE:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"DE fired",0


; debug
CPU_exception_DB:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"DB fired",0


; NMI
CPU_exception_NMI:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"NMI fired",0


; breakpoint
CPU_exception_BP:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"BP fired",0


; overflow
CPU_exception_OF:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"OF fired",0


; bound range
CPU_exception_BR:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"BR fired",0


; invalid opcode
CPU_exception_UD:

	mov	rdi, .msg

	str	rsi
	sub	rsi, 0x40
	shr	rsi, 4

	mov	rdx, [rsp]
	mov rcx, [rsp + 8]

	call	Printf
	cli
	hlt

.msg 	db 10,"UD fired on CPU %u with error code 0x%x RIP 0x%a",0


; device not available
CPU_exception_NM:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"NM fired",0


; double failt
CPU_exception_DF:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"DF fired",0


; invalid TSS
CPU_exception_TS:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"TS fired",0


; segment not present
CPU_exception_NP:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"NP fired",0


; stack
CPU_exception_SS:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"SS fired",0


; general protection
CPU_exception_GP:

	mov	rdi, .msg

	str	rsi
	sub	rsi, 0x40
	shr	rsi, 4

	mov	rdx, [rsp]
	mov rcx, [rsp + 8]

	call	Printf
	cli
	hlt

.msg 	db 10,"GP fired on CPU %u with error code 0x%x RIP 0x%a",0

; page fault
CPU_exception_PF:

	mov	rdi, .msg

	str	rsi
	sub	rsi, 0x40
	shr	rsi, 4

	mov	rdx, cr2
	mov rcx, [rsp + 8]

	call	Printf
	cli
	hlt

.msg 	db 10,"PF fired on CPU %u with cr2 0x%a and RIP 0x%a",0


; x87-floating point
CPU_exception_MF:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"MF fired",0


; alignment check
CPU_exception_AC:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"AC fired",0


; machine check
CPU_exception_MC:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"MC fired",0


; SIMD - floating point
CPU_exception_XF:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"XF fired",0


; security
CPU_exception_SX:

	mov	rdi, .msg
	call	Printf
	cli
	hlt

.msg 	db 10,"SX fired",0


;*********************************************************************
; APIC timer calibration IRQs

IRQ_spurious_APIC:
	; return
	iretq

IRQ_dummy_APIC:
	; save trashed registers
	push	rax
	push	rcx
	push	rdx

	; get APIC base address
	mov	ecx, 0x1b
	rdmsr
	mov	rdx, 0xfffff000
	and	rax, rdx

	; send EOI to APIC
	mov	[rax + 0xb0], DWORD 0

	; restore trashed registers
	pop	rdx
	pop	rcx
	pop	rax

	; return
	iretq

IRQ_APIC_PIT_calibrator:
	; save trashed registers
	push	rax
	push	rcx
	push	rdx

	; check if 1st or 2nd call
	mov	rdx, .done_once
	mov	eax, [rdx]
	or	eax, eax
	jnz	.2nd_call

	; on 1st call
	; set done once variable
	mov	[rdx], DWORD 1

	; set the APIC timer
	; get APIC base address
	mov	ecx, 0x1b
	rdmsr
	mov	rdx, 0xfffff000
	and	rax, rdx

	; set the initial count register to max
	mov	edx, 0x7fffffff
	mov	[rax + 0x380], edx

	; jump over 2nd call section
	jmp	.finish

.2nd_call:
	; reset call counter
	mov	[rdx], DWORD 0

	; get the APIC current count
	; get APIC base address
	mov	ecx, 0x1b
	rdmsr
	mov	rdx, 0xfffff000
	and	rax, rdx

	; read the current count register and save it to the global variable
	mov	edx, [rax + 0x390]
	mov	rcx, g_ticks
	mov	[rcx], edx

	; disable timer (set current count to 0)
	mov	edx, 0
	mov	[rax + 0x380], edx

.finish:
	; send EOI
	mov	al, 0x20
	mov	dx, 0x20
	out	dx, al

	; restore trashed registers
	pop	rdx
	pop	rcx
	pop	rax

	; finished, return
	iretq

.done_once	dd 0;







